package tester;

//import java.lang.annotation.Annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;

import org.apache.log4j.Logger;

import tester.cobertura.Instrument;
import tester.utilities.DependencyArranger;
import tester.utilities.ErrorReport;
import tester.utilities.Inspector;
import tester.utilities.Printer;
import tester.utilities.TestRunner;
import tester.utilities.TesterUtility;

//import org.apache.log4j.Logger;

/**
 * Copyright 2007, 2008, 2009 Viera K. Proulx, Weston Josey
 * This program is distributed under the terms of the 
 * GNU Lesser General Public License (LGPL)
 */

/**
 * <P>
 * A test harness that compares arbitrary objects for extensional equality.
 * </P>
 * <P>
 * It catches exceptions in tests - the remaining tests are omitted.
 * </P>
 * <P>
 * It uses the visitor pattern to accept test cases.
 * </P>
 * <P>
 * Displays the data even if no tests are present.
 * </P>
 * 
 * @author Viera K. Proulx, Weston Jossey
 * @since 21 March 2008, 11 June 2008, 24 June 2008, 16 October 2008, 11
 *        November 2008, 23 November 2008, 12 December 2008, 20 January 2009,
 *        Feb 1 2009
 */
@Instrument
public class Tester {
	private static final Logger logger = Logger.getLogger(Tester.class);

	/** A <code>String</code> that records the results for all failed tests */
	public String testResults = "Test results: \n--------------\n";

	/** A <code>String</code> that records all test results */
	public StringBuilder fullTestResults = new StringBuilder("Full test results: "
			+ "\n-------------------\n");

	/** the total number of tests */
	public int numberOfTests;

	/** the total number of errors */
	public int errors;

	/** the name of the current test */
	public String testname;
	
	/** store whether or not its a full report*/
	public boolean full;

	/** An instance of the Inspector to use throughout */
	public Inspector inspector = new Inspector();

	/** start with no tests and no failures */
	public Tester() {
		this.numberOfTests = 0;
		this.errors = 0;
		this.testname = "";
	}

	/*--------------------------------------------------------------------*/
	/*------------- TEST SELECTION AND REPORTING SECTION -----------------*/
	/*--------------------------------------------------------------------*/
	/**
	 * <p>
	 * Super magic happens here....
	 * </p>
	 * <p>
	 * Report only the results of all failed tests in the class whose instance
	 * is given as the parameter.
	 * </p>
	 * 
	 * @param f
	 *            the instance of a class that defines all tests and the data
	 *            for them
	 */
	public void runAnyTests(Object f) {
		runAnyTests(f, false);
	}

	/**
	 * <p>
	 * Super magic happens here....
	 * </p>
	 * <p>
	 * Report only the results of tests in the class whose instance is given as
	 * the parameter.
	 * </p>
	 * <p>
	 * If the class extends the <code>IExamples</code> all tests within the
	 * <code>test</code> method are performed. Otherwise we locate all methods
	 * with names that start with <code>test</code> and consume one parameter of
	 * the type <code>Tester</code> and perform the desired tests.
	 * </p>
	 * 
	 * @param f
	 *            the instance of a class that defines all tests and the data
	 *            for them
	 * @param full
	 *            true if full test report is desired
	 */
	public void runAnyTests(Object f, boolean full) {
		runAnyTests_V2(f, full, false);
	}

	/**
	 * <p>
	 * Super magic happens here....
	 * </p>
	 * <p>
	 * Report only the results of tests in the class whose instance is given as
	 * the parameter.
	 * </p>
	 * <p>
	 * If the class extends the <code>IExamples</code> all tests within the
	 * <code>test</code> method are performed. Otherwise we locate all methods
	 * with names that start with <code>test</code> and consume one parameter of
	 * the type <code>Tester</code> and perform the desired tests.
	 * </p>
	 * 
	 * @param f
	 *            The instance of a class that defines all tests and the data
	 *            for them
	 * @param full
	 *            true if full test report is desired
	 * @param printall
	 *            true if <code>Examples</code> class data is to be printed
	 * @since Feb 1 2009
	 */
	public void runAnyTests_V2(Object f, boolean full, boolean printall){
		this.numberOfTests = 0;
		this.full = full;
		boolean failed = false;
		
		if (printall) {
			// pretty-print the 'Examples' class data when desired
			System.out.println(f.getClass().getName() + ":\n---------------");
			System.out.println(Printer.produceString(f) + "\n---------------");
		}
		
		// check if the 'Examples' class extends 'IExamples'
		// if yes, run its 'tests' method
		Class<?>[] interfaces = f.getClass().getInterfaces();
		boolean foundInterface = false;
		
		for (Class<?> c : interfaces) {
			if (c.getName().equals("tester.IExamples")) {
				runTests((IExamples) f);
				foundInterface = true;
			}
		}
		
		if(!foundInterface){
			TestRunner runner = new TestRunner(f, this);
			runner.run();	
		}
		
		if(full)
			this.fullTestReport();
		else
			this.testReport();
		done(failed);
		
	}
	
	/**
	 * <p> Deprecated since Feb 1 2009.  We no longer need this method because
	 * we run the TestRunner through the V2 version of this method.
	 * <p>
	 * Super magic happens here....
	 * </p>
	 * <p>
	 * Report only the results of tests in the class whose instance is given as
	 * the parameter.
	 * </p>
	 * <p>
	 * If the class extends the <code>IExamples</code> all tests within the
	 * <code>test</code> method are performed. Otherwise we locate all methods
	 * with names that start with <code>test</code> and consume one parameter of
	 * the type <code>Tester</code> and perform the desired tests.
	 * </p>
	 * 
	 * @param f
	 *            The instance of a class that defines all tests and the data
	 *            for them
	 * @param full
	 *            true if full test report is desired
	 * @param printall
	 *            true if <code>Examples</code> class data is to be printed
	 */
	@Deprecated
	public void runAnyTests(Object f, boolean full, boolean printall) {
		this.numberOfTests = 0; // number of tests run
		boolean failed = false; // any tests failed?

		if (printall) {
			// pretty-print the 'Examples' class data when desired
			System.out.println(f.getClass().getName() + ":\n---------------");
			System.out.println(Printer.produceString(f) + "\n---------------");
		}

		// check if the 'Examples' class extends 'IExamples'
		// if yes, run its 'tests' method
		Class<?>[] interfaces = f.getClass().getInterfaces();
		boolean foundInterface = false;

		for (Class<?> c : interfaces) {
			if (c.getName().equals("tester.IExamples")) {
				runTests((IExamples) f);
				foundInterface = true;
			}
		}

		// otherwise use reflection to find all test methods
		if (!foundInterface) {

			// find all methods that start with the String 'test...'
			// and consume one argument of the type Tester
			// Never used- Wes Jossey
			// Class<?>[] classparams = new Class[] { this.getClass() };

			Collection<Method> testMethods = this.findTestMethods(f,
					"Your class does not define any method with the header\n"
							+ " boolean test...(Tester t)");

			if (testMethods != null) {
				testMethods = DependencyArranger
						.sortByDependencies(testMethods);
			}

			/* Find any setup methods */
			Collection<Method> classSetupMethods = this
					.findMethodsWithAnnotation(f, ClassSetup.class, "");
			/* If we have some setup methods */
			if (classSetupMethods != null) {
				/* Sort by their dependencies */
				classSetupMethods = DependencyArranger
						.sortByDependencies(classSetupMethods);
			} else {
				/* Otherwise, keep us from having null pointers */
				classSetupMethods = new ArrayList<Method>();
			}

			/* Find any pre-method setup methods */
			Collection<Method> methodsBeforeTests = this
					.findMethodsWithAnnotation(f, BeforeTests.class, "");
			/* If we have pre-test setup methods */
			if (methodsBeforeTests != null) {
				/* Sort the methods by their dependencies */
				methodsBeforeTests = DependencyArranger
						.sortByDependencies(methodsBeforeTests);
			} else {
				/* Otherwise, keep us from having null pointers */
				methodsBeforeTests = new ArrayList<Method>();
			}

			/* Find any post-method tear downs */
			Collection<Method> methodsAfterTests = this
					.findMethodsWithAnnotation(f, AfterTests.class, "");
			/* If we have some post-test tear down methods */
			if (methodsAfterTests != null) {
				/* Sort the tear down methods */
				methodsAfterTests = DependencyArranger
						.sortByDependencies(methodsAfterTests);
			} else {
				/* Otherwise, keep us from having null pointers */
				methodsAfterTests = new ArrayList<Method>();
			}

			/* Find any class-wide tear downs */
			Collection<Method> classTearDownMethods = this
					.findMethodsWithAnnotation(f, ClassTearDown.class, "");
			/* If we have some class wide tear down methods */
			if (classTearDownMethods != null) {
				classTearDownMethods = DependencyArranger
						.sortByDependencies(classTearDownMethods);
			} else {
				/* Keep us from having null pointers */
				classTearDownMethods = new ArrayList<Method>();
			}

			// make sure there are tests to run
			if (testMethods != null) {
				// invoke every method that starts with 'test'
				// and accepts one Tester argument (ignore the resulting
				// boolean)
				Object[] args = new Object[] { this };

				/* Run all of the class-wide setup methods */

				for (Method setup : classSetupMethods) {
					if (setup != null) {
						try {
							setup.invoke(f, (Object[]) null);
						} catch (Throwable e) {
							System.err.println("Unable to invoke method.  "
									+ "Make sure the method has "
									+ "no arguments");
						}
					}
				}

				try {

					/* For each test method */
					for (Method testMethod : testMethods) {
						/* Run all of the pre-test setup methods */
						for (Method beforeTests : methodsBeforeTests) {
							/* Invoke each test */
							if (beforeTests != null) {
								beforeTests.invoke(f, (Object[]) null);
							}
						}

						/* Invoke the test method */
						if (testMethod != null) {
							testMethod.invoke(f, args);
						}

						/* Run all of the tear down methods */
						for (Method afterTests : methodsAfterTests) {
							if (afterTests != null) {
								afterTests.invoke(f, (Object[]) null);
							}
						}

					}

				}
				// catch all exceptions
				catch (Throwable e) {
					this.errors = this.errors + 1;
					System.out.println("Threw exception during test "
							+ this.numberOfTests);
					e.printStackTrace();
					failed = true;
				}

				// print the test results at the end
				finally {

					/* For each class-wide tear down method */
					for (Method tearDown : classTearDownMethods) {
						if (tearDown != null) {
							try {
								tearDown.invoke(f, (Object[]) null);
							} catch (Throwable e) {
								System.err.println("Unable to invoke method.  "
										+ "Make sure the method has "
										+ "no arguments");
							}
						}
					}

					if (full)
						this.fullTestReport();
					else
						this.testReport();
					done(failed);
				}
			}
		}
	}

	/**
	 * <P>
	 * Run the tests, accept the class to be tested as a visitor
	 * </P>
	 * <P>
	 * An alternative method for running all tests.
	 * </p>
	 * 
	 * @param f
	 *            the class to be tested -- it defines the method
	 *            <code>void tests(Tester t)</code> that invokes all test cases.
	 */
	protected void runTests(IExamples f) {
		runTests(f, false);
	}

	/**
	 * <P>
	 * Run the tests, accept the class to be tested as a visitor
	 * </P>
	 * <P>
	 * An alternative method for running all tests.
	 * </p>
	 * 
	 * @param f
	 *            the class to be tested -- it defines the method
	 *            <code>void tests(Tester t)</code> that invokes all test cases.
	 * @param full
	 *            true if full test report is desired
	 */
	protected void runTests(IExamples f, boolean full) {
		this.numberOfTests = 0;
		boolean failed = false;

		System.out.println("Examples class:\n---------------");
		System.out.println(Printer.produceString(f) + "\n---------------");

		try {
			f.tests(this);
		} catch (Throwable e) { // catch all exceptions
			this.errors = this.errors + 1;
			System.out.println("Threw exception during test "
					+ this.numberOfTests);
			e.printStackTrace();
			failed = true;
		} finally {
			if (full)
				this.fullTestReport();
			else
				this.testReport();
			done(failed);
		}
	}

	/**
	 * On completion of all tests print the count of number of tests and the
	 * number of failures.
	 */
	protected void done(boolean failed) {
		if (failed) {
			this.reportErrors(testname, "caused RuntimeException", "unkown");
		}
		/*
		 * if (this.errors > 0) System.out.print("Test summary:\nFailed " +
		 * this.errors + " out of "); else
		 * System.out.print("Test summary:\nPassed all "); System.out.println
		 * (this.n + " tests.");
		 */
	}

	/*--------------------------------------------------------------------*/
	/*------------- TEST EVALUATION SECTION: Public API ------------------*/
	/*--------------------------------------------------------------------*/

	/*--------------- Tolerance for comparing inexact numbers  -----------*/

	/**
	 * Set the relative tolerance for the comparison of inexact numbers
	 * 
	 * @param epsilon
	 *            the desired tolerance
	 */
	public boolean setTolerance(double epsilon) {
		Inspector.TOLERANCE = epsilon;
		return epsilon > 0;
	}

	/*-------- Delegate the 'same' method to the Inspector class ---------*/

	/**
	 * <P>
	 * Provide a general extensional equality comparison for arbitrary pair of
	 * objects
	 * </P>
	 * <P>
	 * Inspector invokes the user-defined <code>same</code> method, if the
	 * corresponding classes implement the <code>ISame</code> interface.
	 * </P>
	 * 
	 * @param obj1
	 *            the first object
	 * @param obj2
	 *            the second object
	 * @return true if the two objects represent the same data
	 */
	public boolean same(Object obj1, Object obj2) {
		return inspector.isSame(obj1, obj2);
	}

	/*------------------------ Test definitions  -------------------------*/

	public boolean success() {
		return success("");
	}

	/**
	 * A simple test that can be used as an easy way of demonstrating a success.
	 * 
	 * @param testName
	 *            the name of the test
	 * @return always returns true
	 */
	public boolean success(String testName) {
		/*
		 * Wrap checkExpect with a true/true combo
		 */
		return this.reportSuccess(testName);
		//return checkExpect(true, true, testName);
	}

	/**
	 * A simple test that can be used as an easy way of demonstrating a failure.
	 * 
	 * @return always returns false
	 */
	public boolean fail() {
		/*
		 * Wrap checkExpect with a true/false combo
		 */
		return fail("");
	}

	/**
	 * A simple test that can be used as an easy way of demonstrating a failure.
	 * 
	 * @param testName
	 *            the name of the test
	 * @return always returns false
	 */
	public boolean fail(String testName) {
		return this.reportErrors(testName);
		//return checkExpect(true, false, testName);
	}

	/**
	 * Test that only reports success or failure
	 * 
	 * @param result
	 *            the test result
	 */
	public boolean checkExpect(boolean result) {
		return checkExpect(result, "");
	}

	/**
	 * Test that only reports success or failure
	 * 
	 * @param testname
	 *            the name of this test
	 * @param result
	 *            the test result
	 */
	public boolean checkExpect(boolean result, String testname) {
		this.testname = testname;
		if (!result) {
			// add the report of failure
			return this.report(false, testname + ": error -- no blame -- \n",
					false, true);
		}
		// add the report of success
		else {
			return this.addSuccess(testname + ": success \n");
		}
	}

	/**
	 * Test that only reports success or failure
	 * 
	 * @param result
	 *            the test result that should fail
	 */
	public boolean checkFail(boolean result) {
		return checkFail(result, "");
	}

	/**
	 * Test that only reports success or failure
	 * 
	 * @param result
	 *            the test result that should fail
	 */
	public boolean checkFail(boolean result, String testname) {
		return checkExpect(!result, "Failure expected: \n" + testname);
	}

	/*------------------------ <T> vs. <T>  ---------------------------*/

	/**
	 * Test that compares two objects of any kind - should fail
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected value of the type T
	 */
	public <T> boolean checkFail(T actual, T expected) {
		return this.checkFail(actual, expected, "");
	}

	/**
	 * Test that compares two objects of any kind - should fail
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected value of the type T
	 * @param testname
	 *            the name of this test
	 */
	public <T> boolean checkFail(T actual, T expected, String testname) {
		this.testname = testname;
		return this.report(!(inspector.isSame(actual, expected)),
				"Failure expected: \n" + testname, actual, expected);
	}

	/**
	 * Test that compares two objects of any kind
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected value of the type T
	 */
	public <T> boolean checkExpect(T actual, T expected) {
		return checkExpect(actual, expected, "");
	}

	/**
	 * Test that compares two objects of any kind
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected value of the type T
	 * @param testname
	 *            the name of this test
	 */
	public <T> boolean checkExpect(T actual, T expected, String testname) {
		this.testname = testname;
		return this.report(inspector.isSame(actual, expected), testname,
				actual, expected);
	}

	/**
	 * Test that verifies that when the given object invokes the given method
	 * with the given arguments, it throws the expected exception with the
	 * expected message.
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param args
	 *            An array of arguments for the method - use Array of length 0
	 *            if no arguments are needed
	 * @param e
	 *            An instance of the expected exception -- including the
	 *            expected message
	 */
	public <T> boolean checkExpect(T object, String method, Object[] args,
			Exception e) {
		return checkExpect(object, method, args, e, "");
	}

	/**
	 * Test that verifies that when the given object invokes the given method
	 * with the given arguments, it throws the expected exception with the
	 * expected message.
	 * 
	 * *Note* This is the Ellipsis version of another method
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param e
	 *            An instance of the expected exception -- including the
	 *            expected message
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param args
	 *            An <em>Ellipsis</em> of arguments for the method
	 */
	public <T> boolean checkExpect(Exception e, T object, String method,
			Object... args) {
		return checkExpect(object, method, args, e);
	}

	/**
	 * Test that verifies that when the given object invokes the given method
	 * with the given arguments, it throws the expected exception with the
	 * expected message.
	 * 
	 * Note, this is the Ellipsis version.
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param testname
	 *            The description of this test
	 * @param e
	 *            An instance of the expected exception -- including the
	 *            expected message
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param args
	 *            An <em>Ellipsis</em> of arguments for the method
	 */
	public <T> boolean checkExpect(String testname, Exception e, T object,
			String method, Object... args) {
		return checkExpect(object, method, args, e, testname);
	}

	/**
	 * Test that verifies that when the given object invokes the given method
	 * with the given arguments, it throws the expected exception with the
	 * expected message.
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param args
	 *            An array of arguments for the method - use Array of length 0
	 *            if no arguments are needed
	 * @param e
	 *            An instance of the expected exception -- including the
	 *            expected message
	 * @param testname
	 *            The description of this test
	 */
	public <T> boolean checkExpect(T object, String method, Object[] args,
			Exception e, String testname) {
		this.testname = testname;
		int length;
		
		// create an Array of the argument types
		//Check for null;
		if(args != null){
			length = Array.getLength(args);
		}else{
			length = 0;
		}
		Class<?>[] parameters = new Class[length];

		for (int i = 0; i < length; i++) {
			parameters[i] = args[i].getClass();
		}

		// get the type of the expected exception
		Class<?> exceptClass = e.getClass();
		String exceptName = exceptClass.getName();
		String exceptMessage = e.getMessage();
		try {
			// find the method
			Method meth = this.findMethod(object, method, parameters, testname);

			// allow access to protected and private methods
			meth.setAccessible(true);

			Object result = meth.invoke(object, args);

			// if the invocation succeeds, the test fails because
			// it does not throw the expected exception
			if (result == null)
				return this.report(false,
						testname + "\n invocation did not throw any exception "
								+ "\n  method name: " + meth.getName()
								+ "\n  object class: "
								+ object.getClass().getName()
								+ "\n  result: void" + ": "
								+ Printer.produceString(result)
								+ "\n  expected exception was: \n    class: "
								+ exceptName + "\n    message: "
								+ exceptMessage + "\n", result,
						"exception expected");
			else
				return this.report(false,
						testname + "\n invocation did not throw any exception "
								+ "\n  method name: " + meth.getName()
								+ "\n  object class: "
								+ object.getClass().getName() + "\n  result: "
								+ result.getClass().getName() + ": "
								+ Printer.produceString(result)
								+ "\n  expected exception was: \n    class: "
								+ exceptName + "\n    message: "
								+ exceptMessage + "\n", result,
						"exception expected");
		} catch (Throwable exception) {

			String excName;
			String excMessage;
			if (exception.getCause() != null) {
				excName = exception.getCause().getClass().getName();
				excMessage = exception.getCause().getMessage();
			} else {
				excName = exception.getClass().getName();
				excMessage = exception.getMessage();
			}

			// check that the method threw an exception of the desired type
			if (excName.equals(exceptName)) {

				// check whether the message is correct
				if (excMessage.equals(exceptMessage)) {

					// report correct exception, correct message -- test
					// succeeds
					return this.addSuccess(testname
							+ "\n correct exception: \n" + "" + " class: "
							+ exceptName + "\n correct message: "
							+ exceptMessage + "\n");
				} else {

					// report correct exception, incorrect message -- test fails
					return this.report(false, testname
							+ "\n correct exception: \n" + "" + " class: "
							+ exceptName + "\n incorrect message: "
							+ excMessage + "\n", "message produced: "
							+ excMessage, "message expected: " + exceptMessage);
				}
			} else {
				// report that method invocation threw an exception of the wrong
				// type
				return this.report(false, testname
						+ "\n incorrect exception was thrown: ",
						"exception thrown:   " + excName,
						"exception expected: " + exceptName);
			}
		}
	}

	// --------- METHOD INVOCATION TESTS -------------------------------------
	/**
	 * Invoke the method with the given name on a given object with the given
	 * arguments - check if it produces the expected value
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param args
	 *            An array of arguments for the method - use Array of length 0
	 *            if no arguments are needed
	 * @param expected
	 *            The expected result of the method being tested
	 */
	public <T> boolean checkExpect(T object, String method, Object[] args,
			Object expected) {
		return checkExpect(object, method, args, expected, "");
	}

	/**
	 * Invoke the method with the given name on a given object with the given
	 * arguments - check if it produces the expected value
	 * 
	 * Note: This is the Ellipsis version
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param object
	 *            The type of the object that invokes the given method
	 * @param method
	 *            The name of the method to test
	 * @param expected
	 *            The expected result of the method being tested
	 * @param args
	 *            An <em>Ellipsis</em> of arguments for the method
	 */
	public <T> boolean checkExpect(Object expected, T object, String method,
			Object... args) {
		return checkExpect(object, method, args, expected, "");
	}

	/**
	 * Invoke the method with the given name on a given object with the given
	 * arguments - check if it produces the expected value
	 * 
	 * 
	 * Note: This is the Ellipsis version
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param expected
	 *            The expected result of the method being tested
	 * @param testname
	 *            The description of this test
	 * @param args
	 *            An <em>Ellipsis</em> of arguments for the method
	 */
	public <T> boolean checkExpect(String testname, Object expected, T object,
			String method, Object... args) {
		return checkExpect(object, method, args, expected, testname);
	}

	/**
	 * Invoke the method with the given name on a given object with the given
	 * arguments - check if it produces the expected value
	 * 
	 * @param <T>
	 *            The type of the object that invokes the given method
	 * @param object
	 *            The object that invokes the method to be tested
	 * @param method
	 *            The name of the method to test
	 * @param args
	 *            An array of arguments for the method - use Array of length 0
	 *            if no arguments are needed
	 * @param expected
	 *            The expected result of the method being tested
	 * @param testname
	 *            The description of this test
	 */
	public <T> boolean checkExpect(T object, String method, Object[] args,
			Object expected, String testname) {
		this.testname = testname;
		// create an Array of the argument types
		int length = Array.getLength(args);
		Class<?>[] parameters = new Class[length];

		// Never read- Wes Jossey
		// String[] paraNames = new String[length];

		for (int i = 0; i < length; i++) {
			parameters[i] = args[i].getClass();
		}

		String parlist = "(";
		for (Class<?> parameter : parameters)
			parlist = parlist + (parameter.getName()) + ",";

		parlist = parlist.substring(0, parlist.length() - 1) + ")";

		try {
			// find the method to be invoked by 'object' with the name 'method'
			// and parameters in the classes given by 'parameters'
			Method meth = findMethod(object, method, parameters, testname);

			if (meth == null) {
				return report(false, testname + "\nNo method with the name "
						+ method + " found\n", "Failed to invoke method "
						+ object.getClass().getName() + "." + method + parlist,
						expected);
			} else {
				// allow access to protected and private methods
				meth.setAccessible(true);

				String testmessage = testname + "\n"
						+ Printer.produceString(object) + "\n invoked method "
						+ method + " in the class "
						+ object.getClass().getName() + "\n with arguments "
						+ Printer.produceString(args) + "\n";

				return checkExpect(meth.invoke(object, args), expected,
						testmessage);
			}
		} catch (Throwable exception) {
			// String testmessage = testname + "\n" +
			String testmessage = testname + "\n"
					+ Printer.produceString(object) + "\n invoked method "
					+ method + " in the class " + object.getClass().getName()
					+ "\n with arguments " + Printer.produceString(args) + "\n";

			// report that the method threw an exception
			boolean result = this.report(false, testmessage
					+ "\nthrew an excception ", object, Printer
					.produceString(args));
			exception.printStackTrace();
			return result;
		}
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the same as one of the expected values
	 * 
	 * Note: This is the Ellipsis version
	 * 
	 * @param <T>
	 *            The type of the objects being compared
	 * @param actual
	 *            The computed value of the type T
	 * @param expected
	 *            An <em>Ellipsis</em> of the expected values of the type T
	 */
	public <T> boolean checkOneOf(T actual, T... expected) {
		return checkOneOf(actual, expected, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the same as one of the expected values
	 * 
	 * @param <T>
	 *            The type of the objects being compared
	 * @param actual
	 *            The computed value of the type T
	 * @param expected
	 *            The expected values of the type T
	 * @param testname
	 *            The name of this test
	 */
	public <T> boolean checkOneOf(T actual, T[] expected, String testname) {
		this.testname = testname;
		for (int i = 0; i < Array.getLength(expected); i++) {
			if (inspector.isSame(actual, expected[i]))
				return this.report(true, testname, actual, expected[i]);
		}
		return this.report(false, testname + "\nNo matching value found "
				+ "among the list of expected values", actual, expected);
	}

	/**
	 * * Test that determines whether the value of the given object (of any
	 * kind) is the same as one of the expected values
	 * 
	 * Note: This is the Ellipsis version
	 * 
	 * @param <T>
	 * @param testname
	 *            The name of this test
	 * @param actual
	 *            The computed value of the type T
	 * @param expected
	 *            An <em>Ellipsis</em> of the expected values of the type T
	 */
	public <T> boolean checkOneOf(String testname, T actual, T... expected) {
		return checkOneOf(actual, expected, testname);
	}

	/*---------------- None of the choices checks -----------------------*/

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is not equal to any of the expected values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected values of the type T
	 */
	public <T> boolean checkNoneOf(T actual, T... expected) {
		return checkNoneOf(actual, expected, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is not equal to any of the expected values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected values of the type T
	 */
	public <T> boolean checkNoneOf(String testname, T actual, T... expected) {
		return checkNoneOf(actual, expected, testname);
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is not equal to any of the expected values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param expected
	 *            the expected values of the type T
	 */
	public <T> boolean checkNoneOf(T actual, T[] expected, String testname) {
		this.testname = testname;
		for (int i = 0; i < Array.getLength(expected); i++) {
			if (inspector.isSame(actual, expected[i]))
				return this.report(false,
						"Matching value found in none of test\n" + testname,
						actual, expected[i]);
		}
		return this
				.report(
						true,
						testname
								+ "\nNo matching value found among the list of excluded values",
						actual, expected);
	}

	/*---------------- Numeric (mixed) range checks -----------------------*/

	/**
	 * Test that determines whether the value of the given numerical object (of
	 * any kind) is the range between low (inclusive) and high (exclusive)
	 * values. Allows for comparison of mixed numeric types.
	 * 
	 * @param actual
	 *            the computed value of the type Number
	 * @param low
	 *            the lower limit of the type Number (inclusive) for this range
	 * @param high
	 *            the upper limit of the type Number (exclusive) for this range
	 */
	public <T> boolean checkNumRange(Number actual, Number low, Number high) {
		return checkNumRange(actual, low, high, "");
	}

	/**
	 * Test that determines whether the value of the given numerical object (of
	 * any kind) is the range between low (inclusive) and high (exclusive)
	 * values. Allows for comparison of mixed numeric types.
	 * 
	 * @param actual
	 *            the computed value of the type Number
	 * @param low
	 *            the lower limit of the type Number (inclusive) for this range
	 * @param high
	 *            the upper limit of the type Number (exclusive) for this range
	 */
	public <T> boolean checkNumRange(Number actual, Number low, Number high,
			String testname) {
		return checkNumRange(actual, low, high, true, false, testname);
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low and high values. User must specify whether the
	 * low and high bounds are inclusive or exclusive.
	 * 
	 * @param actual
	 *            the computed value of the type Number
	 * @param low
	 *            the lower limit of the type Number (inclusive) for this range
	 * @param high
	 *            the upper limit of the type Number (exclusive) for this range
	 * @param lowIncl
	 *            is the low limit inclusive for this range?
	 * @param highIncl
	 *            is the upper limit inclusive for this range?
	 */
	public <T> boolean checkNumRange(Number actual, Number low, Number high,
			boolean lowIncl, boolean highIncl) {
		return checkNumRange(actual, low, high, lowIncl, highIncl, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low and high values. User must specify whether the
	 * low and high bounds are inclusive or exclusive.
	 * 
	 * @param actual
	 *            the computed value of the type Number
	 * @param low
	 *            the lower limit of the type Number (inclusive) for this range
	 * @param high
	 *            the upper limit of the type Number (exclusive) for this range
	 * @param lowIncl
	 *            is the low limit inclusive for this range?
	 * @param highIncl
	 *            is the upper limit inclusive for this range?
	 */
	public <T> boolean checkNumRange(Number actual, Number low, Number high,
			boolean lowIncl, boolean highIncl, String testname) {
		this.testname = testname;
		boolean within = true;
		boolean aboveLow = ((Double) actual.doubleValue()).compareTo(low
				.doubleValue()) > 0;
		boolean belowHigh = ((Double) actual.doubleValue()).compareTo(high
				.doubleValue()) < 0;

		if (lowIncl)
			aboveLow = aboveLow
					|| ((Double) actual.doubleValue()).compareTo(low
							.doubleValue()) == 0;

		if (highIncl)
			belowHigh = belowHigh
					|| ((Double) actual.doubleValue()).compareTo(high
							.doubleValue()) == 0;

		within = aboveLow && belowHigh;
		// System.out.println("Compared " + actual.toString() +
		// " within [" + low.toString() + ", " + high.toString() + "): " +
		// within);
		if (within)
			return this.report(within, testname, actual, low, high);
		else
			return this.report(within, testname
					+ "\nActual value is not within the [low high) range.",
					actual, low, high);
	}

	/*-------------------- General range checks --------------------------*/

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low (inclusive) and high (exclusive) values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T (inclusive) for this range
	 * @param high
	 *            the upper limit of the type T (exclusive) for this range
	 */
	public <T> boolean checkRange(Comparable<T> actual, T low, T high) {
		return checkRange(actual, low, high, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low (inclusive) and high (exclusive) values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T (inclusive) for this range
	 * @param high
	 *            the upper limit of the type T (exclusive) for this range
	 * @param testname
	 *            the name of this test
	 */
	public <T> boolean checkRange(Comparable<T> actual, T low, T high,
			String testname) {
		return checkRange(actual, low, high, true, false, testname);
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low and high values. User must specify whether the
	 * low and high bounds are inclusive or exclusive.
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T for this range
	 * @param high
	 *            the upper limit of the type T for this range
	 * @param lowIncl
	 *            is the low limit inclusive for this range?
	 * @param highIncl
	 *            is the upper limit inclusive for this range?
	 */
	public <T> boolean checkRange(Comparable<T> actual, T low, T high,
			boolean lowIncl, boolean highIncl) {
		return checkRange(actual, low, high, lowIncl, highIncl, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low and high values. User must specify whether the
	 * low and high bounds are inclusive or exclusive.
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T for this range
	 * @param high
	 *            the upper limit of the type T for this range
	 * @param lowIncl
	 *            is the low limit inclusive for this range?
	 * @param highIncl
	 *            is the upper limit inclusive for this range?
	 * @param testname
	 *            the name of this test
	 */
	public <T> boolean checkRange(Comparable<T> actual, T low, T high,
			boolean lowIncl, boolean highIncl, String testname) {
		this.testname = testname;
		boolean within = true;
		boolean aboveLow = true;
		boolean belowHigh = true;
		String ls = "(";
		String hs = ")";

		if (lowIncl) {
			aboveLow = actual.compareTo(low) >= 0;
			ls = "[";
		} else
			aboveLow = actual.compareTo(low) > 0;

		if (highIncl) {
			belowHigh = actual.compareTo(high) <= 0;
			hs = "]";
		} else
			belowHigh = actual.compareTo(high) < 0;

		within = aboveLow && belowHigh;
		// System.out.println("Compared " + actual.toString() +
		// " within " + ls + low.toString() + ", " + high.toString() + hs +
		// ": " + within);
		if (within)
			return this.report(within, testname, actual, low, high);
		else
			return this.report(within, testname
					+ "\nActual value is not within the " + ls + "low high"
					+ hs + " range.", actual, low, high);
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low (inclusive) and high (exclusive) values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T (inclusive) for this range
	 * @param high
	 *            the upper limit of the type T (exclusive) for this range
	 * @param testname
	 *            the name of this test
	 */

	public <T> boolean checkRange(T actual, T low, T high, Comparator<T> comp,
			String testname) {
		return checkRange(actual, low, high, true, false, comp, testname);
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low (inclusive) and high (exclusive) values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T (inclusive) for this range
	 * @param high
	 *            the upper limit of the type T (exclusive) for this range
	 */
	public <T> boolean checkRange(T actual, T low, T high, Comparator<T> comp) {
		return checkRange(actual, low, high, true, false, comp, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low (inclusive) and high (exclusive) values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T (inclusive) for this range
	 * @param high
	 *            the upper limit of the type T (exclusive) for this range
	 * @param lowIncl
	 *            is the low limit inclusive for this range?
	 * @param highIncl
	 *            is the upper limit inclusive for this range?
	 * @param comp
	 *            The <code>Comparator</code> used to compare the values
	 */
	public <T> boolean checkRange(T actual, T low, T high, boolean lowIncl,
			boolean highIncl, Comparator<T> comp) {
		return checkRange(actual, low, high, lowIncl, highIncl, comp, "");
	}

	/**
	 * Test that determines whether the value of the given object (of any kind)
	 * is the range between low (inclusive) and high (exclusive) values
	 * 
	 * @param <T>
	 *            the type of the objects being compared
	 * @param actual
	 *            the computed value of the type T
	 * @param low
	 *            the lower limit of the type T (inclusive) for this range
	 * @param high
	 *            the upper limit of the type T (exclusive) for this range
	 * @param lowIncl
	 *            is the low limit inclusive for this range?
	 * @param highIncl
	 *            is the upper limit inclusive for this range?
	 * @param comp
	 *            The <code>Comparator</code> used to compare the values
	 * @param testname
	 *            the name of this test
	 */
	public <T> boolean checkRange(T actual, T low, T high, boolean lowIncl,
			boolean highIncl, Comparator<T> comp, String testname) {
		this.testname = testname;
		boolean within = true;
		boolean aboveLow = true;
		boolean belowHigh = true;
		String ls = "(";
		String hs = ")";

		if (lowIncl) {
			aboveLow = comp.compare(actual, low) >= 0;
			ls = "[";
		} else
			aboveLow = comp.compare(actual, low) > 0;

		if (highIncl) {
			belowHigh = comp.compare(actual, high) <= 0;
			hs = "]";
		} else
			belowHigh = comp.compare(actual, high) < 0;

		within = aboveLow && belowHigh;

		// System.out.println("Compared " + actual.toString() +
		// " within " + ls + low.toString() + ", " + high.toString() + hs +
		// ": " + within);

		if (within)
			return this.report(within, testname, actual, low, high);
		else
			return this.report(within, testname
					+ "\nActual value is not within the " + ls + "low high"
					+ hs + " range.", actual, low, high);
	}

	/*--------------------------------------------------------------------*/
	/*---------- Check Effect Section ------------------------------------*/
	/*--------------------------------------------------------------------*/
	/**
	 * <h2>This is the preliminary attempt at being able to test void methods in
	 * a single method.</h2>
	 * <p>
	 * This method takes an object, a method name, a populated arguments
	 * wrapper, and a variable number of fields that are expected to change. The
	 * tester will deep clone the object, resulting in new references to all
	 * attributes. The tester then modifies the expected fields to the expected
	 * new values, and compares them against the actual modifications made by
	 * the void method.
	 * </p>
	 * <p>
	 * This test is more accurate than the traditional JUnit tests because it
	 * will detect subtle effects in code that were unexpected to the
	 * programmer.
	 * </p>
	 * <p>
	 * The tester will "pretty-print" the two objects if the test fails.
	 * </p>
	 * 
	 * @param <T>
	 *            type of the object to test
	 * @param object
	 *            object to test
	 * @param methodName
	 *            method name to run
	 * @param args
	 *            wrapper containing all of the arguments for the method
	 * @param effectFields
	 *            a variable number of EffectField objects
	 * @return returns true if the method passed the test, otherwise false.
	 */
	public <T> boolean checkEffect(T object, String methodName, Arguments args,
			EffectField... effectFields) {

		/* This avoids null pointer problems */
		if (args == null) {
			args = new Arguments();
		}

		/* Clone the object with modified fields */
		T cloneOfObject = new TesterUtility<T>(object, effectFields).getClone();

		try {

			Method m = findMethodIncludeInherited(object, methodName,
					(args == null ? new Arguments().getArgumentClasses() : args
							.getArgumentClasses()), "");

			/* Invoke the object with the given arguments */
			m.invoke(object, args.getArgs());
			return checkExpect(object, cloneOfObject);
		} catch (SecurityException e) {
			logger.error(e.getLocalizedMessage());
			throw new RuntimeException("Security error.  Halting\n"
					+ e.getLocalizedMessage());
		} catch (IllegalArgumentException e) {
			logger.error(e.getLocalizedMessage());
			throw new RuntimeException("Wrong arguments\n"
					+ e.getLocalizedMessage());
		} catch (IllegalAccessException e) {
			logger.error(e.getLocalizedMessage());
			throw new RuntimeException("Illegal access\n"
					+ e.getLocalizedMessage());
		} catch (InvocationTargetException e) {
			logger.error(e.getLocalizedMessage());
			throw new RuntimeException("Invocation Target Exception\n"
					+ e.getLocalizedMessage());
		}
		// catch (NoSuchMethodException e) {
		// logger.error(e.getLocalizedMessage());
		// throw new RuntimeException(
		// "No such method with the given argument exists.");
		// }
	}

	/**
	 * In the same mold as checkEffect, the checkEffectOneOf method performs the
	 * same functionality but allows for multiple outcomes. This could be quite
	 * useful for outcomes which have a random distribution of different
	 * possible outcomes.
	 * 
	 * @param <T>
	 *            type of the object to test
	 * @param object
	 *            object to test
	 * @param methodName
	 *            name of the method to run
	 * @param args
	 *            method arguments
	 * @param oneOfEffectFields
	 *            a multi-dimensional array that allows for multiple arrays of
	 *            effectFields
	 * @return returns true if the test succeeds, otherwise returns false
	 */
	public <T> boolean checkEffectOneOf(T object, String methodName,
			Arguments args, EffectField[][] oneOfEffectFields) {
		boolean returnVal = false;

		/* Clone the object */
		T cloneOfObject = new TesterUtility<T>(object).getClone();

		for (int i = 0; i < oneOfEffectFields.length; i++) {
			EffectField[] effectFields = new EffectField[oneOfEffectFields[i].length];

			/* Construct the effectFields array */
			for (int j = 0; j < oneOfEffectFields[i].length; j++) {
				effectFields[j] = oneOfEffectFields[i][j];
			}

			try {
				cloneOfObject = Modify.modify(cloneOfObject, effectFields);
			} catch (NoSuchFieldException e1) {
				throw new RuntimeException(e1.getLocalizedMessage());
			} catch (IllegalAccessException e1) {
				throw new RuntimeException(e1.getLocalizedMessage());
			} catch (Exception e) {
				throw new RuntimeException(e.getLocalizedMessage());
			}

			/* Construct the parameter types to find the method */
			Class<?>[] paramTypes = new Class<?>[args.getArgs().length];
			int k = 0;
			for (Object o : args.getArgs()) {
				paramTypes[k++] = o.getClass();
			}

			try {
				Method m = object.getClass().getDeclaredMethod(methodName,
						paramTypes);
				/* Invoke the object with the given arguments */
				m.invoke(object, args.getArgs());
				returnVal = checkExpect(object, cloneOfObject);

				if (returnVal)
					return returnVal;

			} catch (SecurityException e) {
				logger.error(e.getLocalizedMessage());
				throw new RuntimeException("Security error.  Halting\n"
						+ e.getLocalizedMessage());
			} catch (NoSuchMethodException e) {
				logger.error(e.getLocalizedMessage());
				throw new RuntimeException("No such method\n"
						+ e.getLocalizedMessage());
			} catch (IllegalArgumentException e) {
				logger.error(e.getLocalizedMessage());
				throw new RuntimeException("Wrong arguments\n"
						+ e.getLocalizedMessage());
			} catch (IllegalAccessException e) {
				logger.error(e.getLocalizedMessage());
				throw new RuntimeException("Illegal access\n"
						+ e.getLocalizedMessage());
			} catch (InvocationTargetException e) {
				logger.error(e.getLocalizedMessage());
				throw new RuntimeException("Invocation Target Exception\n"
						+ e.getLocalizedMessage());
			}

		}

		return returnVal;
	}

	/*--------------------------------------------------------------------*/
	/*---------- HELPERS FOR THE TEST EVALUATION SECTION -----------------*/
	/*--------------------------------------------------------------------*/

	/**
	 * Find the method with the given name for the type of the given object
	 * (including all super classes), that consumes parameters of the given
	 * types. Account for any autoboxing of primitive types.
	 * 
	 * @param object
	 *            The object expected to invoke the method
	 * @param method
	 *            The name of the method to invoke
	 * @param parameters
	 *            The parameter types for the method invocation
	 * @return The instance of the declared method
	 */
	private <T> Method findMethodIncludeInherited(T object, String method,
			Class<?>[] parameters, String testName) {

		Method[] allMethods = findAllMethods(object.getClass());
		/* For each method, check to see if it's the "right" method */
		/* If it is, then return that method, otherwise keep going */
		for(Method m : allMethods){
			if (this.matchParams(m.getParameterTypes(), parameters)) {
				m.setAccessible(true);
				return m;
			}
		}

		/* If we get here, then we didn't find a valid method to work with */
		testname = testname + "\nNo method with the name " + method
				+ " found\n";
		return null;

	}

	/**
	 * Find the method with the given name for the type of the given object,
	 * that consumes parameters of the given types. Account for any autoboxing
	 * of primitive types
	 * 
	 * @param object
	 *            The object expected to invoke the method
	 * @param method
	 *            The name of the method to invoke
	 * @param parameters
	 *            The parameter types for the method invocation
	 * @return The instance of the declared method
	 */
	private <T> Method findMethod(T object, String method,
			Class<?>[] parameters, String testname) {

		Method[] allMethods = findAllMethods(object.getClass());
		ArrayList<Method> allNamed = new ArrayList<Method>();

		// make a list of all methods with the given name
		for (Method elt : allMethods)
			if (elt.getName().equals(method))
				allNamed.add(elt);

		// / add the test to compare parameters -- invocation with int works!!
		if (allNamed.size() > 0) {
			for (Method m : allNamed) {
				if (this.matchParams(m.getParameterTypes(), parameters))
					return m;
			}
			// no methods matched the given parameter list
			testname = testname + "\nNo method with the name " + method
					+ " had a matching argument list\n";
			return null;
		}
		// no methods found with the given name
		else {
			testname = testname + "\nNo method with the name " + method
					+ " found\n";
			return null;
		}
	}
	

	/**
	 * Finds all of the methods for a particular class, including those that are part of a 
	 * super class.  This will stop when it hits java.lang.Object.  While traversing the 
	 * methods, it also marks all of the methods to accessible so that we can utilize them
	 * elsewhere in our code.  This should be abstracted out to a custom reflector (similar
	 * to what we already have) in the future.
	 * @param c class to collect methods from
	 * @return the resulting array of methods found
	 */
	private Method[] findAllMethods(Class<?> c){
		Class<?> classToSurvey = c;
		
		ArrayList<Method> list = new ArrayList<Method>();
		//While we have a class to examine
		while (classToSurvey != null && classToSurvey != Object.class) {
			//Get all public and private methods
			Method[] allMethods = classToSurvey.getDeclaredMethods();
			//For each method, set it to accessible and add it to the list
			for (Method m : allMethods) {
				m.setAccessible(true);
				list.add(m);
			}
			
			//Get the super class and loop
			classToSurvey = classToSurvey.getSuperclass();
		}
		
		//Convert the ArrayList to an array and return
		return list.toArray(new Method[0]);
	}

	/* Re-use found methods */
	private Method[] allMethods = null;

	/**
	 * Finds methods which are marked with a particular annotation.
	 * @param <T> type of object 
	 * @param object object to examine
	 * @param annotationClass annotation to match against
	 * @param testname current test name
	 * @return an ArrayList containing all of the methods with the
	 * given annotation
	 */
	private <T> ArrayList<Method> findMethodsWithAnnotation(T object,
			Class<? extends Annotation> annotationClass, String testname) {

		if (allMethods == null) {
			allMethods = findAllMethods(object.getClass());
		}

		ArrayList<Method> foundMethods = new ArrayList<Method>();

		// make a list of all methods with the given name
		for (Method method : allMethods) {

			if (method.isAnnotationPresent(annotationClass)) {
				foundMethods.add(method);
				
				//Be aware that this is redundant
				method.setAccessible(true); 
			}
		}

		if (foundMethods.size() > 0) {
			// found test methods that matched the given parameter list
			testname = testname + "Found " + foundMethods.size()
					+ " test methods";

			return foundMethods;
		}
		// no test methods that matched the given parameter list
		else {
			testname = testname + "\nNo method with the name test..."
					+ " found in the class " + object.getClass().getName()
					+ "\n";
			return null;
		}

	}

	/**
	 * Find all test methods (with the name that starts with test...) in the
	 * class determined by the instance of the given object, that consumes a
	 * <code>Tester</code> argument.
	 * 
	 * @param object
	 *            The object expected to invoke the method
	 * @param testname
	 *            The <code>String</code> that records test diagnosis info
	 * @return an <code>ArrayList</code> of all test methods
	 */
	private <T> ArrayList<Method> findTestMethods(T object, String testname) {

		Method[] allMethods = findAllMethods(object.getClass());
		ArrayList<Method> allNamed = new ArrayList<Method>();
		Class<?>[] testerParam = new Class[] { this.getClass() };

		// make a list of all methods with the given name
		for (Method method : allMethods) {

			if (method.getName().startsWith("test")
					&& this
							.matchParams(method.getParameterTypes(),
									testerParam)
					|| method.getAnnotation(TestMethod.class) != null) {
				allNamed.add(method);
				method.setAccessible(true);
			}
		}

		System.out.println("Found " + allNamed.size() + " test methods");

		if (allNamed.size() > 0) {
			// found test methods that matched the given parameter list
			testname = testname + "Found " + allNamed.size() + " test methods";

			return allNamed;
		}
		// no test methods that matched the given parameter list
		else {
			testname = testname + "\nNo method with the name test..."
					+ " found in the class " + object.getClass().getName()
					+ "\n";
			return null;
		}
	}

	/**
	 * See if the list of <CODE>Class</CODE instances
	 *   that represent the parameter list for the method to invoke
   *   matches the method definition with the same name
	 * 
	 * @param parInput
	 *            <CODE>Array</CODE> of <CODE>Class</CODE instances
   *   that represent the input parameter list
	 * @param parDefined
	 *            <CODE>Array</CODE> of <CODE>Class</CODE instances
   *   that represent the parameter list for a method with matching name
	 * @return true if the parameter lists represent the same classes allowing
	 *         for primitive types to match their wrapper classes.
	 */
	private boolean matchParams(Class<?>[] parInput, Class<?>[] parDefined) {
		if (Array.getLength(parInput) != Array.getLength(parDefined))
			return false;
		else {
			for (int i = 0; i < Array.getLength(parInput); i++) {
				String in = parInput[i].getName();
				String def = parDefined[i].getName();
				if (!in.equals(def)) {
					if (Inspector.isWrapperClass(def)) {
						if (!isWrapperMatch(in, def))
							return false;
						else
							return true;
					}
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Check whether the primitive type with the name <CODE>in</CODE> matches
	 * the wrapper type with the name <CODE>def</CODE>.
	 * 
	 * @param in
	 *            The name of the primitive type to match
	 * @param def
	 *            The name of the wrapper class to match
	 * @return true if the primitive type matches its wrapper class
	 */
	private boolean isWrapperMatch(String in, String def) {
		if (def.equals("java.lang.Integer") && in.equals("int"))
			return true;
		else if (def.equals("java.lang.Short") && in.equals("short"))
			return true;
		else if (def.equals("java.lang.Long") && in.equals("long"))
			return true;
		else if (def.equals("java.lang.Byte") && in.equals("byte"))
			return true;
		else if (def.equals("java.lang.Character") && in.equals("char"))
			return true;
		else if (def.equals("java.lang.Double") && in.equals("double"))
			return true;
		else if (def.equals("java.lang.Float") && in.equals("float"))
			return true;
		else if (def.equals("java.lang.Double") && in.equals("double"))
			return true;
		else if (def.equals("java.lang.Boolean") && in.equals("boolean"))
			return true;
		else
			return false;
	}

	/*--------------------------------------------------------------------*/
	/*--------------------- TEST REPORTING SECTION -----------------------*/
	/*--------------------------------------------------------------------*/

	/**
	 * General contractor to report test results
	 * 
	 * @param success
	 *            Did the test succeed?
	 * @param testname
	 *            The name of this test
	 * @param actual
	 *            The computed value of the result
	 * @param expected
	 *            The expected value of the result
	 */
	private boolean report(boolean success, String testname, Object actual,
			Object expected) {
		if (success)
			return this.reportSuccess(testname, actual, expected);
		else {
			// get the relevant stack trace for this test case
			String trace = this.getStackTrace();
			return this.reportErrors(testname + "\n" + trace, actual, expected);

		}
	}

	/**
	 * General contractor to report test results for range check
	 * 
	 * @param success
	 *            Did the test succeed?
	 * @param testname
	 *            The name of this test
	 * @param actual
	 *            The computed value of the result
	 * @param low
	 *            The low value (inclusive)of the range
	 * @param high
	 *            The high value (exclusive) of the range
	 */
	private boolean report(boolean success, String testname, Object actual,
			Object low, Object high) {
		if (success)
			return this.reportSuccess(testname, actual, low, high);
		else {
			// get the relevant stack trace for this test case
			String trace = this.getStackTrace();
			return this
					.reportErrors(testname + "\n" + trace, actual, low, high);
		}
	}

	/**
	 * Add a test to the list of failed tests
	 * 
	 * @param testname
	 *            The name of the failed test
	 * @param actual
	 *            The computed value of the test
	 * @param expected
	 *            The expected value of the test
	 */
	private boolean reportErrors(String testname, Object actual, Object expected) {

		// add test report to the error report and the full test report
		return this.addError("Error in test number " + (numberOfTests + 1)
				+ "\n" + testname + "\n" + "actual:     "
				+ Printer.produceString(actual) + "\n" + "expected:   "
				+ Printer.produceString(expected) + "\n");
	}
	
	/**
	 * Generalized reportErrors for the case where we have a Tester.fail()
	 * @param testName The name of the failed test
	 */
	private boolean reportErrors(String testName){
		String trace = this.getStackTrace();
		return this.addError("Error in the test number " + (numberOfTests + 1)
				+ "\n" + testName + "\n" + trace + "\n");
	}

	/**
	 * Add a test to the list of failed tests
	 * 
	 * @param testname
	 *            The name of the failed range test
	 * @param actual
	 *            The computed value of the test
	 * @param low
	 *            The low (inclusive) value of the range
	 * @param high
	 *            The high (exclusive) value of the range
	 */
	private boolean reportErrors(String testname, Object actual, Object low,
			Object high) {

		// add test report to the error report and the full test report
		return this.addError("Error in range test number "
				+ (numberOfTests + 1) + "\n" + testname + "\n" + "actual:     "
				+ Printer.produceString(actual) + "\n" + "low:   "
				+ Printer.produceString(low) + "\n" + "high:   "
				+ Printer.produceString(high) + "\n");
	}

	/**
	 * Produce a formatted String that represent the relevant entries of the
	 * <CODE>StackTrace</CODE> that provides a link to the test case that
	 * produced the error.
	 * 
	 * @return a formatted String representation of the relevant stack trace
	 */
	private String getStackTrace() {
		try {
			// force an exception so you can inspect the stack trace
			throw new ErrorReport("Error trace:");
		} catch (ErrorReport e) { // catch the exception
			// record the original stack trace
			StackTraceElement[] ste = e.getStackTrace();

			// copy only the relevant entries
			int length = Array.getLength(ste);
			StackTraceElement[] tmpSTE = new StackTraceElement[length];
			int ui = 0; // index for the stripped stack trace
			for (int i = 3; i < length; i++) {
				String cname = ste[i].getClassName();
				if (!((cname.startsWith("tester."))
						|| (cname.startsWith("sun.reflect"))
						|| (cname.startsWith("java.lang"))
						|| (cname.startsWith("bluej")) || (cname
						.startsWith("__SHELL")))) {
					tmpSTE[ui] = ste[i];
					ui = ui + 1;
				}
			}

			// we cannot have null entries at the end
			StackTraceElement[] userSTE = new StackTraceElement[ui];
			for (int i = 0; i < ui; i++) {
				userSTE[i] = tmpSTE[i];
			}

			// now set the stack trace so it can be converted to a String
			e.setStackTrace(userSTE);

			StringWriter writer = new StringWriter();
			PrintWriter printwriter = new PrintWriter(writer);
			e.printStackTrace(printwriter);

			return writer.toString();
		}
	}

	/**
	 * Add a test to the list of successful tests
	 * 
	 * @param testname
	 *            The name of the successful test
	 * @param actual
	 *            The computed value of the test
	 * @param expected
	 *            The expected value of the test
	 */
	private boolean reportSuccess(String testname, Object actual,
			Object expected) {

		// add test report to the full test report
		if(full){
			return this.addSuccess("Success in the test number "
					+ (numberOfTests + 1) + "\n" + testname + "\n" + "actual:     "
					+ Printer.produceString(actual) + "\n" + "expected:   "
					+ Printer.produceString(expected) + "\n");
		}else{
			return this.addSuccess("Success in the test number "
					+ (numberOfTests + 1) + "\n" + testname + "\n");
		}
	}
	
	/**
	 * Report a generalized success where there may have been no values 
	 * computed, just a call to Tester.success
	 * @param testName The name of the successful test
	 */
	private boolean reportSuccess(String testName){
		
		return this.addSuccess("Success in the test number " 
				+ (numberOfTests + 1) + "\n" + testName + "\n");
	}

	/**
	 * Add a test to the list of failed tests
	 * 
	 * @param testname
	 *            The name of the failed range test
	 * @param actual
	 *            The computed value of the test
	 * @param low
	 *            The low (inclusive) value of the range
	 * @param high
	 *            The high (exclusive) value of the range
	 */
	private boolean reportSuccess(String testname, Object actual, Object low,
			Object high) {

		// add test report to the full test report
		if(full){
			return this.addSuccess("Success in the range test number "
					+ (numberOfTests + 1) + "\n" + testname + "\n" + "actual:     "
					+ Printer.produceString(actual) + "\n" + "low:   "
					+ Printer.produceString(low) + "\n" + "high:   "
					+ Printer.produceString(high) + "\n");	
		}else{
			return this.addSuccess("Success in the range test number "
					+ (numberOfTests + 1) + "\n" + testname + "\n");
		}
		
	}

	/**
	 * Add the given test successful test result to the full report
	 * 
	 * @param testResult
	 *            The successful test result
	 */
	private boolean addSuccess(String testResult) {
		this.numberOfTests = this.numberOfTests + 1;
		this.fullTestResults = this.fullTestResults.append("\n" + testResult);
		// update the count of all tests
		return true;
	}

	/**
	 * Add the given test failed test result to the full report and to the error
	 * report
	 * 
	 * @param testResult
	 *            The failed test result
	 */
	private boolean addError(String testResult) {
		// update the count of all tests
		this.numberOfTests = this.numberOfTests + 1;
		// update the count of the errors tests
		this.errors = this.errors + 1;
		this.testResults = this.testResults + "\n" + testResult;
		this.fullTestResults = this.fullTestResults.append("\n" + testResult);
		return false;
	}

	/**
	 * Produce a <code>String</code> describing the number of tests that were
	 * run and that failed.
	 */
	private String testCount() {
		String tCount = "";
		// report test totals
		if (this.numberOfTests == 1) {
			tCount = "\nRan 1 test.\n";
		} else if (this.numberOfTests > 1) {
			tCount = "\nRan " + numberOfTests + " tests.\n";
		}

		// report error totals
		if (this.errors == 0) {
			tCount = tCount + "All tests passed.\n\n";
		}
		if (this.errors == 1) {
			tCount = tCount + "1 test failed.\n\n";

		} else if (this.errors > 1) {
			tCount = tCount + this.errors + " tests failed.\n\n";
		}

		return tCount;
	}

	/**
	 * Report on the number and nature of failed tests
	 */
	protected void testReport() {
		System.out.println(testCount() + this.testResults
				+ "\n--- END OF TEST RESULTS ---\n");
	}

	/**
	 * Produce test names and values compared for all tests
	 */
	protected void fullTestReport() {
		System.out.println(testCount() + this.fullTestResults
				+ "\n--- END OF FULL TEST RESULTS ---");
	}

	/*--------------------------------------------------------------------*/
	/*-------------- TESTER INVOCATION HELPERS SECTION -------------------*/
	/*--------------------------------------------------------------------*/

	/**
	 * A hook to run the tester for any object --- needed to run from BlueJ.
	 * Print all fields of the 'Examples' class and a report of all failed
	 * tests.
	 * 
	 * @param obj
	 *            The 'Examples' class instance where the tests are defined
	 */
	public static void run(Object obj) {
		Tester t = new Tester();
		t.runAnyTests_V2(obj, false, true);
	}

	/**
	 * A hook to run the tester for any objects and produce a full test report
	 * 
	 * @param objs
	 *            An array of 'Examples' class instances where tests are defined
	 */
	public static void runFullReport(Object... objs) {
		Tester t = new Tester();
		if(objs != null){
			for(Object obj : objs){
				t.runAnyTests(obj, true);
			}
		}
	}

	/**
	 * A hook to run the tester for any object and produce a full test report
	 * 
	 * @param obj
	 *            The 'Examples' class instance where the tests are defined
	 */
	public static void runReport(Object obj, boolean full, boolean printall) {
		Tester t = new Tester();
		t.runAnyTests_V2(obj, full, printall);
	}
}